package com.rcache.core.config;

import com.rcache.core.cache.BaseCache;
import com.rcache.core.cache.Rcache;
import com.rcache.core.cache.CacheEnum;
import com.rcache.core.cache.strategy.FIFOCache;
import com.rcache.core.cache.strategy.LRUCache;
import com.rcache.core.cache.strategy.MultipleCache;
import com.rcache.core.cache.strategy.lfu.LFUCache;
import com.rcache.core.exception.ConfigurationException;
import com.rcache.core.manager.CacheManager;
import org.apache.log4j.Logger;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 * @desc 缓存配置类
 * @author gzy
 * @time 2022-05-26
 */
public final class CacheConfiguration implements ICacheConfiguration{
    /**最大存活时间，默认10秒*/
    private int maxTimeToLive=10000;
    /**最大空闲时间，默认2秒*/
    private int maxTimeToIdle=2000;
    /**是否永不过期*/
    private boolean eternal=false;
    /**初始容量*/
    protected int initElements=3;
    /**预警容量*/
    protected int warningElements=7;
    /**最大元素数量*/
    private int maxElements=1000;
    /**缓存淘汰策略,默认FIFO*/
    private String cacheStrategy="FIFO";
    /**是否开启磁盘存储*/
    private boolean diskCache=false;
    /**磁盘存储策略,默认定时缓存*/
    private String diskCacheStrategy="default";
    /**指定存储位置,必须指定否则抛出异常*/
    private String diskCachePath;
    /**是否开启分布式缓存*/
    private boolean distribute=false;
    /**master  Port*/
    private String distributePort;
    /**master  Ip*/
    private String distributeIP;
    /**是否开启预警系统*/
    protected boolean warning=false;
    /**预警系统管理员邮箱*/
    protected String managerEmail="";

    private long diskCacheTime=10000;
    private long diskCacheSize=15;


    private void recoverDefault(){
        /**最大存活时间，默认10秒*/
        this.maxTimeToLive=10000;
        /**最大空闲时间，默认2秒*/
        this.maxTimeToIdle=2000;
        /**是否永不过期*/
        this.eternal=false;
        /**初始容量*/
        this.initElements=3;
        /**预警容量*/
        this.warningElements=7;
        /**最大元素数量*/
        this.maxElements=1000;
        /**缓存淘汰策略,默认FIFO*/
        this.cacheStrategy="FIFO";
        /**是否开启磁盘存储*/
        this.diskCache=false;
        /**磁盘存储策略,默认定时缓存*/
        this.diskCacheStrategy="default";
        /**指定存储位置,必须指定否则抛出异常*/
        this.diskCachePath=null;
        /**是否开启分布式缓存*/
        this.distribute=false;
        /**master  Port*/
        this.distributePort=null;
        /**master  Ip*/
        this.distributeIP=null;
        /**是否开启预警系统*/
        this.warning=false;
        /**预警系统管理员邮箱*/
        this.managerEmail=null;

        this.diskCacheTime=10000;
        this.diskCacheSize=15;
    }

    private final Logger LOG= Logger.getLogger(this.getClass());


    public String getDistributePort() {
        return distributePort;
    }

    public void setDistributePort(String distributePort) {
        this.distributePort = distributePort;
    }

    public String getDistributeIP() {
        return distributeIP;
    }

    public void setDistributeIP(String distributeIP) {
        this.distributeIP = distributeIP;
    }

    public int getMaxTimeToLive() {
        return maxTimeToLive;
    }

    public void setMaxTimeToLive(int maxTimeToLive) {
        this.maxTimeToLive = maxTimeToLive;
    }

    public int getMaxTimeToIdle() {
        return maxTimeToIdle;
    }

    public void setMaxTimeToIdle(int maxTimeToIdle) {
        this.maxTimeToIdle = maxTimeToIdle;
    }

    public boolean isEternal() {
        return eternal;
    }

    public void setEternal(boolean eternal) {
        this.eternal = eternal;
    }

    public int getMaxElements() {
        return maxElements;
    }

    public void setMaxElements(int maxElements) {
        this.maxElements = maxElements;
    }

    public boolean isDiskCache() {
        return diskCache;
    }

    public void setDiskCache(boolean diskCache) {
        this.diskCache = diskCache;
    }

    public String getDiskCacheStrategy() {
        return diskCacheStrategy;
    }

    public void setDiskCacheStrategy(String diskCacheStrategy) {
        this.diskCacheStrategy = diskCacheStrategy;
    }

    public String getDiskCachePath() {
        return diskCachePath;
    }

    public void setDiskCachePath(String diskCachePath) {
        this.diskCachePath = diskCachePath;
    }

    public boolean isDistribute() {
        return distribute;
    }

    public void setDistribute(boolean distribute) {
        this.distribute = distribute;
    }

    public String getCacheStrategy() {
        return cacheStrategy;
    }

    public void setCacheStrategy(String cacheStrategy) {
        this.cacheStrategy = cacheStrategy;
    }

    public int getInitElements() {
        return initElements;
    }

    public void setInitElements(int initElements) {
        this.initElements = initElements;
    }

    public int getWarningElements() {
        return warningElements;
    }

    public void setWarningElements(int warningElements) {
        this.warningElements = warningElements;
    }

    public boolean isWarning() {
        return warning;
    }

    public void setWarning(boolean warning) {
        this.warning = warning;
    }

    public String getManagerEmail() {
        return managerEmail;
    }

    public void setManagerEmail(String managerEmail) {
        this.managerEmail = managerEmail;
    }

    @Override
    public Map<String, Rcache> init(String configFilePath) throws ConfigurationException {
        if (configFilePath.equals("default")){
            Map defaultCache=new HashMap();
            FIFOCache cache = null;
            try {
                cache = (FIFOCache) Class.forName(CacheEnum.FIFO.getCacheClassPath()).newInstance();
                cache.setCacheName("default");
            } catch (InstantiationException | ClassNotFoundException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
            defaultCache.put("default",cache);
            return defaultCache;
        }

        //加载properties配置文件
        File checkProperties = checkProperties();
        if (checkProperties!=null){
            LOG.info("找到properties配置文件，正准备加载XML中配置");
            try {
                return loadProperties(checkProperties);
            } catch (ClassNotFoundException e) {
                throw new ConfigurationException("初始化"+cacheStrategy+"缓存淘汰策略异常");
            }catch (IOException e) {
                throw new ConfigurationException("从properties配置文件中加载配置异常");
            } catch (InstantiationException |IllegalAccessException e) {
                throw new ConfigurationException("实例化"+cacheStrategy+"缓存淘汰策略异常");
            }
        }

       /* //加载xml配置文件
        File chekXML = checkXML();
        if (chekXML!=null){
            LOG.info("找到XML配置文件，正准备加载XML中配置");
            return loadXML(chekXML);
        }*/


        return null;
    }

    /**
     * @param propertiesFile
     * @return
     * @desc 加载配置文件
     */
    private Map<String, Rcache> loadProperties(File propertiesFile) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, ConfigurationException {
        LOG.debug("加载Properties文件中……");
        Properties properties=new Properties();
        properties.load(new FileInputStream(propertiesFile));
        String cacheName = properties.getProperty("CacheName");
        Map<String, Rcache> map=new HashMap();
        if (cacheName!=null && cacheName.trim().length()>0){

            String[] cacheNames = cacheName.split(",");
            List multipleCacheList=new ArrayList<>();
             for (String name : cacheNames) {
                 recoverDefault();
                 String maxTimeToLive = properties.getProperty(name + ".maxTimeToLive");
                 String maxTimeToIdle = properties.getProperty(name + ".maxTimeToIdle");
                 String eternal = properties.getProperty(name + ".eternal");
                 String initElements = properties.getProperty(name + ".initElements");
                 String warningElements = properties.getProperty(name + ".warningElements");
                 String maxElements = properties.getProperty(name + ".maxElements");
                 String diskCache = properties.getProperty(name + ".diskCache");
                 String diskCacheStrategy = properties.getProperty(name + ".diskCacheStrategy");
                 String cacheStrategy = properties.getProperty(name + ".cacheStrategy");
                 String diskCachePath = properties.getProperty(name + ".diskCachePath");
                 String distribute = properties.getProperty(name + ".distribute");
                 String distributeIP = properties.getProperty(name + ".distributeIP");
                 String distributePort = properties.getProperty(name + ".distributePort");
                 String warning = properties.getProperty(name + ".warning");
                 String managerEmail = properties.getProperty(name + ".managerEmail");
                 String diskCacheTime = properties.getProperty(name + ".diskCacheTime");
                 String diskCacheSize = properties.getProperty(name + ".diskCacheSize");


                 this.maxTimeToLive=maxTimeToLive!=null? Integer.parseInt(maxTimeToLive):this.maxTimeToLive;
                 this.maxTimeToIdle=maxTimeToIdle!=null? Integer.parseInt(maxTimeToIdle):this.maxTimeToIdle;
                 this.eternal=eternal!=null?  Boolean.parseBoolean(eternal):this.eternal;
                 this.initElements=initElements!=null? Integer.parseInt(initElements):this.initElements;
                 this.warningElements=warningElements!=null? Integer.parseInt(warningElements):this.warningElements;
                 this.maxElements=maxElements!=null? Integer.parseInt(maxElements):this.maxElements;
                 this.diskCache=diskCache!=null? Boolean.parseBoolean(diskCache):this.diskCache;
                 this.diskCacheStrategy=diskCacheStrategy!=null? diskCacheStrategy:this.diskCacheStrategy;
                 this.diskCachePath=diskCachePath!=null? diskCachePath:this.diskCachePath;
                 this.cacheStrategy=cacheStrategy!=null? cacheStrategy:this.cacheStrategy;
                 this.distribute=distribute!=null? Boolean.parseBoolean(distribute):this.distribute;
                 this.distributeIP =distributeIP!=null? distributeIP:this.distributeIP;
                 this.distributePort =distributePort!=null? distributePort:this.distributePort;
                 this.managerEmail =managerEmail!=null? managerEmail:this.managerEmail;
                 this.warning =warning!=null? Boolean.parseBoolean(warning) :this.warning;
                 this.diskCacheTime =diskCacheTime!=null? Long.parseLong(diskCacheTime) :this.diskCacheTime;
                 this.diskCacheSize =diskCacheSize!=null? Long.parseLong(diskCacheSize) :this.diskCacheSize;
//                 System.out.println(this.cacheStrategy+":"+CacheEnum.LRU);
                 if (CacheEnum.FIFO.equals(this.cacheStrategy)){
                     LOG.debug("构建"+name+"为FIFO缓存");
                     FIFOCache cache= (FIFOCache) Class.forName(CacheEnum.FIFO.getCacheClassPath()).newInstance();
                     conventionConfig(map, name, cache);
                 }else if (CacheEnum.LRU.equals(this.cacheStrategy)){
                     LOG.debug("构建"+name+"为LRU缓存");
                     LRUCache cache= (LRUCache) Class.forName(CacheEnum.LRU.getCacheClassPath()).newInstance();
                     conventionConfig(map, name, cache);
                 }else if (CacheEnum.LFU.equals(this.cacheStrategy)){
                     LOG.debug("构建"+name+"为LFU缓存");
                     LFUCache cache= (LFUCache) Class.forName(CacheEnum.LFU.getCacheClassPath()).newInstance();

                     conventionConfig(map, name, cache);
                 }else if (CacheEnum.Multiple.equals(this.cacheStrategy)){
                     MultipleCache cache= (MultipleCache) Class.forName(CacheEnum.Multiple.getCacheClassPath()).newInstance();
                     LOG.debug("构建"+name+"为Multiple缓存");
                     cache.setCacheName(name);
                     String caches = properties.getProperty(name + ".cacheNames");
                     if (caches==null||caches.length()<1){
                         throw new ConfigurationException("非法配置MultipleCache");
                     }
                     String[] names = caches.split(",");
                     for(String every : names){
                         cache.cacheNames.add(every);
                     }
                     multipleCacheList.add(cache);
                 }

             }
             multipleConfig(map, multipleCacheList);
        }else {
            FIFOCache cache = (FIFOCache) Class.forName(CacheEnum.FIFO.getCacheClassPath()).newInstance();
            map.put("default",cache);
        }
        return map;
    }

    /**
     * @desc 多个缓存策略配置
     * @param map
     * @param multipleCacheList
     */
    private void multipleConfig(Map<String, Rcache> map, List<MultipleCache> multipleCacheList) throws ConfigurationException {
        for (MultipleCache cache:multipleCacheList) {
            List<String> names = cache.cacheNames;
            for (String every:names) {
                Rcache otherCache = map.get(every);
                if (otherCache==null){
                    throw new ConfigurationException("非法配置MultipleCache");
                }
                System.out.println(otherCache);
                cache.addRcaches(otherCache);
            }
            map.put(cache.getCacheName(),cache);
        }
    }


    /**
     * @desc 常规策略配置
     * @param map
     * @param name
     * @param cache
     */
    private void conventionConfig(Map<String, Rcache> map, String name, BaseCache cache) {
        cache.setMaxTimeToLive(this.maxTimeToLive);
        cache.setMaxTimeToIdle(this.maxTimeToIdle);
        cache.setEternal(this.eternal);
        cache.setInitElements(this.initElements);
        cache.setWarningElements(this.warningElements);
        cache.setMaxElements(this.maxElements);
        cache.setDiskCache(this.diskCache);
        cache.setDiskCacheStrategy(this.diskCacheStrategy);
        cache.setDiskCachePath(this.diskCachePath);
        cache.setDistribute(this.distribute);
        cache.setCacheName(name);
        cache.setDistributeIP(this.distributeIP);
        if (this.distributePort!=null){
            cache.setDistributePort(Integer.parseInt(this.distributePort));
        }
        cache.setWarning(this.warning);
        cache.setManagerEmail(this.managerEmail);
        cache.setDiskCacheSize(this.diskCacheSize);
        cache.setDiskCacheTime(this.diskCacheTime);
        map.put(name, cache);
    }

    /**
     * @param chekXML
     * @return
     * @desc 加载配置文件
     */
    private Map<String, Rcache> loadXML(File chekXML) {
        return null;
    }

    private File checkProperties() {
        URL ss = this.getClass().getClassLoader().getResource("");
        String classUrl = CacheManager.class.getClassLoader().getResource("").getPath();
        File rcache=new File(classUrl+"rcache.properties");
//        System.out.println(rcache.getPath());
        return rcache;
    }

    /**
     * @desc 检查class目录下是否存在rcache.xml
     * @return rcache.xml的file对象
     */
    private File checkXML() {
        String classUrl = this.getClass().getClassLoader().getResource("/").getPath();
        File rcache=new File(classUrl+"\\rcache.xml");
        return rcache;
    }
}